/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.interpreter.frontends.ntriples;

import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author Laurent WOUTERS
 */
public class GraphLoader {
    private static final String defaultRDFGraphs = "http://www.xowl.org/interpreter/rdfgraphs/";
    
    private static final String PN_CHARS_BASE = "[A-Z]|[a-z]|[\\u00C0-\\u00D6]|[\\u00D8-\\u00F6]|[\\u00F8-\\u02FF]|[\\u0370-\\u037D]|[\\u037F-\\u1FFF]|[\\u200C-\\u200D]|[\\u2070-\\u218F]|[\\u2C00-\\u2FEF]|[\\u3001-\\uD7FF]|[\\uF900-\\uFDCF]|[\\uFDF0-\\uFFFD]";
    private static final String PN_CHARS_U    = "(" + PN_CHARS_BASE + ")|_";
    private static final String PN_CHARS      = "(" + PN_CHARS_U + ")|-|[0-9]|\\u00B7|[\\u0300-\\u036F]|[\\u203F-\\u2040]";
    private static final String PN_LOCAL      = "(" + PN_CHARS_U + "|[0-9])(((" + PN_CHARS + ")|\\.)*(" + PN_CHARS + "))?";
    private static final String expFullIRI = "<([^>]+)>";
    private static final String expNodeID = "_:" + PN_LOCAL;
    private static final String expLitTyped = "\"([^\"]*)\"(\\^\\^" + expFullIRI + ")?";
    private static final String expLitNum = "(\\+|-)?\\d+(\\.\\d*)?";
    private static final String expBlanck = "(\n|\r|\n\r|\\s)+";
    
    private xowl.interpreter.frontends.OntologyListener listener;
    private xowl.interpreter.impl.AggregatedEvaluator evaluator;
    private xowl.interpreter.backends.rdf.RDFGraph graph;
    private String baseIRI;
    private xowl.lang.owl2.Ontology ontology;
    private Map<String, xowl.interpreter.backends.rdf.RDFSubjectNode> anonymousNodes;

    public xowl.interpreter.backends.rdf.RDFGraph getGraph() { return graph; }
    public xowl.lang.owl2.Ontology getOntology() { return ontology; }

    public GraphLoader(xowl.interpreter.impl.AggregatedEvaluator evaluator, xowl.interpreter.frontends.OntologyListener listener, xowl.interpreter.backends.rdf.RDFGraph graph) {
        this.listener = listener;
        this.evaluator = evaluator;
        this.graph = graph;
        this.anonymousNodes = new HashMap<String, xowl.interpreter.backends.rdf.RDFSubjectNode>();
    }
    
    public void load(java.io.Reader reader) {
        java.util.Random rand = new java.util.Random();
        String value = defaultRDFGraphs + Integer.toHexString(rand.nextInt()) + "#";
        baseIRI = value;
        ontology = listener.resolveOntology(listener.getIRI(baseIRI.substring(0, baseIRI.length() - 1)));
        xowl.interpreter.frontends.RewindableStream stream = new xowl.interpreter.frontends.RewindableStream(reader);
        xowl.interpreter.frontends.Tokenizer tokenizer = new xowl.interpreter.frontends.Tokenizer(stream);
        while (true) {
            tokenizer.matches(expBlanck);
            xowl.interpreter.backends.rdf.RDFNode n1 = getNode(tokenizer);
            if (n1 == null)
                break;
            xowl.interpreter.backends.rdf.RDFNode n2 = getNode(tokenizer);
            xowl.interpreter.backends.rdf.RDFNode n3 = getNode(tokenizer);
            tokenizer.matches(expBlanck);
            tokenizer.matches("\\.");
            xowl.interpreter.backends.rdf.RDFTriple triple = new xowl.interpreter.backends.rdf.RDFTriple((xowl.interpreter.backends.rdf.RDFSubjectNode)n1, (xowl.interpreter.backends.rdf.RDFProperty)n2, n3);
            graph.onChange(evaluator, new xowl.interpreter.backends.rdf.RDFChange(triple, ontology, true));
        }
    }
    
    private xowl.interpreter.backends.rdf.RDFNode getNode(xowl.interpreter.frontends.Tokenizer tokenizer) {
        tokenizer.matches(expBlanck);
        String value = tokenizer.matches(expFullIRI);
        if (value != null) {
            xowl.lang.owl2.IRI iri = listener.getIRI(value.substring(1, value.length() - 1));
            return graph.getEntityNode(iri);
        }
        value = tokenizer.matches(expNodeID);
        if (value != null) {
            String id = value.substring(2);
            xowl.interpreter.backends.rdf.RDFSubjectNode node = anonymousNodes.get(id);
            if (node != null)
                return node;
            xowl.lang.owl2.AnonymousIndividual ind = new xowl.lang.owl2.AnonymousIndividual();
            ind.setNodeID(id);
            node = graph.getBlankNode(ind);
        }
        value = tokenizer.matches(expLitTyped);
        if (value != null)
            return getLiteralNode(value);
        value = tokenizer.matches(expLitNum);
        if (value != null)
            return getLiteralNode(value);
        return null;
    }
    
    private xowl.interpreter.backends.rdf.RDFLiteralNode getLiteralNode(String content) {
        if (content.contains("^^")) {
            // Typed literal
            String[] parts = content.split("\\^\\^", -1);
            String value = parts[0].substring(1, parts[0].length() - 1);
            String type = null;
            if (parts[1].isEmpty()) type = xowl.interpreter.impl.OWLDatatype.xsdString;
            else type = parts[1].substring(1, parts[1].length() - 1);
            xowl.lang.runtime.Literal literal = new xowl.lang.runtime.Literal();
            literal.setLexicalValue(value);
            literal.setMemberOf(listener.getDatatype(type));
            return new xowl.interpreter.backends.rdf.RDFLiteralNode(literal);
        } else {
            // Implicitly typed
            xowl.lang.runtime.Literal literal = new xowl.lang.runtime.Literal();
            if (content.startsWith("\"")) {
                // Implicit string
                literal.setLexicalValue(content.substring(1, content.length() - 1));
                literal.setMemberOf(listener.getDatatype(xowl.interpreter.impl.OWLDatatype.xsdString));
            } else if (content.contains(".")) {
                literal.setLexicalValue(content);
                literal.setMemberOf(listener.getDatatype(xowl.interpreter.impl.OWLDatatype.xsdFloat));
            }
            else {
                literal.setLexicalValue(content);
                literal.setMemberOf(listener.getDatatype(xowl.interpreter.impl.OWLDatatype.xsdInteger));
            }
            return new xowl.interpreter.backends.rdf.RDFLiteralNode(literal);
        }
    }
}